﻿using System.Linq;
using System.Reflection;
using Cubicle.Excel.Shared;
using Cubicle.Nuget.Core;
using Cubicle.Nuget.Gui;
using ExcelDna.Integration;

namespace Cubicle.Nuget.Functions
{
    public class NugetFunctions
    {
        // Nlog
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public static ClientManager FeedManager;
        private static NugetConfigForm _configForm;

        public static void Init(ClientManager feedManager)
        {
            FeedManager = feedManager;
        }

        #region Commands

        [ExcelCommand(MenuName = "&NuGet", MenuText = "Check for &Package updates...")]
        public static void AutoUpdateAll()
        {
            foreach (var client in FeedManager)
                foreach (var p in client.Manager.LocalRepository.GetPackages())
                    if (client.UpdatePackage(p, true))
                        _logger.Info("Package updated. Feed = {0}, Id = {1}", client.Name, p.Id);
                    else
                        _logger.Info("No updates found. Feed = {0}, Id = {0}", client.Name, p.Id);
            _logger.Info("Update complete.");
        }

        [ExcelCommand(MenuName = "&NuGet", MenuText = "Reinstall all add-ins...")]
        public static void Reinstall()
        {
            FeedManager.DefaultClient.ReinstallToBin();
        }

        [ExcelCommand(MenuName = "&NuGet", MenuText = "Configure...")]
        public static void Configure()
        {
            if (_configForm == null) _configForm = new NugetConfigForm();
            _configForm.Show();
        }

        //[ExcelCommand(MenuName = "&NuGet", MenuText = "&Documenation")]
        //public static void Documenation()
        //{
        //    System.Diagnostics.Process.Start(NugetAddIn.Config.DocumentationLink);
        //}

        //[ExcelCommand(MenuName = "&NuGet", MenuText = "Package &Manager")]
        //public static void PackageManager()
        //{
        //    var packageManager = new PackageManager();
        //    packageManager.Show();
        //}

        //[ExcelCommand(MenuName = "&NuGet", MenuText = "&About")]
        //public static void About()
        //{
        //    new AboutBox().Show();
        //}

        #endregion

        #region Feed management

        /// <summary>
        /// Register a new feed
        /// </summary>
        /// <param name="alias">The alias to associate with this feed</param>
        /// <param name="address">The address of the feed source</param>
        /// <param name="isDefault">(Optional) Set to true to make this the default feed</param>
        /// <returns>Success/Failure</returns>
        [ExcelFunction(
            Name = "nugetConnectFeed",
            Category = "Nuget (Feeds)",
            Description = "Create an Nuget Feed",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = true,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static bool ConnectFeed(string alias, string address, bool isDefault = false)
        {
            // Register feed
            _logger.Info("Registering feed. Address = {0}, Alias = {1}", address, alias);
            return FeedManager.AddFeed(alias, isDefault, address);
        }

        /// <summary>
        /// Set the default feed to use when the feed alias is omitted from a worksheet function.
        /// </summary>
        /// <param name="alias">The new default feed alias.</param>
        /// <returns>Success/Failure</returns>
        [ExcelFunction(
            Name = "nugetSetDefaultFeed",
            Category = "Nuget (Feeds)",
            Description = "Specify the name of the default Nuget Feed",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static bool SetDefaultFeed(string alias)
        {
            return FeedManager.SetDefault(alias);
        }

        /// <summary>
        /// Returns the alias of the default feed.
        /// </summary>
        /// <returns>The alias of the default feed.</returns>
        [ExcelFunction(
            Name = "nugetGetDefaultFeed",
            Category = "Nuget (Feeds)",
            Description = "Retrieve the default Nuget Feed",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = true,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static string GetDefaultFeed()
        {
            return FeedManager.DefaultClient.Name;
        }

        /// <summary>
        /// Retrieve a list of registered feeds.
        /// </summary>
        /// <returns>A list of registered feeds.</returns>
        [ExcelFunction(
            Name = "nugetListFeeds",
            Category = "Nuget (Feeds)",
            Description = "List Nuget Feeds",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] ListFeeds()
        {
            return FeedManager.ToRange(true, x => new
                {
                    Alias = x.Name,
                    Address = x.Uri,
                    Root = x.Manager.LocalRepository.Source
                });
        }

        #endregion

        #region Enumerate packages

        /// <summary>
        /// List available package names from a feed.
        /// 
        /// If a feed contains multiple versions with the same name, that name will only be returned once.
        /// To get version information on a specific package, use the 'nugetDescribe' worksheet function.
        /// </summary>
        /// <param name="alias">(Optional) The alias of the feed to search. Default is used when omitted.</param>
        /// <param name="take">(Optional) The number of hits to return. Default returns 10. Limited to 100.</param>
        /// <param name="skip">(Optional) The number of hits to skip before the first item is returned. Default is 0</param>
        /// <returns>Available package names from a feed</returns>
        [ExcelFunction(
            Name = "nugetListAvailable",
            Category = "Nuget (List)",
            Description = "List remote packages",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] GetPackages(int take, int skip = 0, string alias = "")
        {
            if (take == 0) take = 10;
            var result = FeedManager[alias].GetPackages();
            return result
                .Skip(skip)
                .Take(take)
                .Select(x => x.Id)
                .Distinct()
                .ToRange();
        }

        /// <summary>
        /// List installed packages
        /// </summary>
        /// <param name="alias">(Optional) The alias of the feed to search. Default is used when omitted.</param>
        /// <returns>List installed packages</returns>
        [ExcelFunction(
            Name = "nugetListInstalled",
            Category = "Nuget (List)",
            Description = "List installed packages",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] GetInstalled(string alias = "")
        {
            return FeedManager[alias].GetInstalled().ToRange(true);
        }

        /// <summary>
        /// List available updates
        /// </summary>
        /// <param name="alias">(Optional) The alias of the feed to search. Default is used when omitted.</param>
        /// <returns>List available updates</returns>
        [ExcelFunction(
            Name = "nugetListUpdates",
            Category = "Nuget (List)",
            Description = "List packages with updates",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] GetUpdates(string alias = "")
        {
            return FeedManager[alias].GetUpdates().ToRange(true);
        }

        /// <summary>
        /// Find a specific package
        /// </summary>
        /// <param name="alias">(Optional) The alias of the feed to search. Default is used when omitted.</param>
        /// <returns>Found packages</returns>
        [ExcelFunction(
            Name = "nugetFind",
            Category = "Nuget (List)",
            Description = "List packages with updates",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] FindPackage(string id, string alias = "")
        {
            return FeedManager[alias].Find(id).ToRange(true);
        }

        /// <summary>
        /// Search repository using a keyword
        /// </summary>
        /// <param name="alias">(Optional) The alias of the feed to search. Default is used when omitted.</param>
        /// <param name="keyword">The keyword to use in the search</param>
        /// <returns>Found packages</returns>
        [ExcelFunction(
            Name = "nugetSearch",
            Category = "Nuget (List)",
            Description = "Search repository using a keyword",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] Search(string keyword, string alias = "")
        {
            return FeedManager[alias].Search(keyword).ToRange(true);
        }

        #endregion

        #region Package description

        /// <summary>
        /// Returns the version of the Nuget Excel Add-In
        /// </summary>
        /// <returns></returns>
        [ExcelFunction(
            Name = "nugetVersion",
            Category = "Nuget (Info)",
            Description = "Get the version of the NuGet Excel Add-In.",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static string NugetVersion()
        {
            return Assembly.GetExecutingAssembly().GetName().Version.ToString();
        }

        /// <summary>
        /// Returns package metadata
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Package metadata</returns>
        [ExcelFunction(
            Name = "nugetDescribe",
            Category = "Nuget (Info)",
            Description = "Describe a nuget package",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] DescribePackage(string packageId, string alias = "")
        {
            return FeedManager[alias][packageId].xlDescriptions();
        }

        /// <summary>
        /// Returns a list of all files in the package
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="version">A package version</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>A list of all files in the package.</returns>
        [ExcelFunction(
            Name = "nugetGetFiles",
            Category = "Nuget (Info)",
            Description = "List all the files in the package",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object GetFiles(string packageId, string version, string alias = "")
        {
            var versionMatched = FeedManager[alias][packageId, version];

            // Check for package Id match
            if (versionMatched == null) return "Failed: PackageId not found.";

            return versionMatched.xlFiles();
        }

        /// <summary>
        /// Returns package dependencies
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="version">Package version</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Returns package dependencies</returns>
        [ExcelFunction(
            Name = "nugetGetDependencies",
            Category = "Nuget (Info)",
            Description = "List Nuget package dependencies",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] GetPackageDependencies(string packageId, string version, string alias = "")
        {
            return FeedManager[alias][packageId, version].xlDependencies();
        }

        /// <summary>
        /// Returns .Net assembly references defined in a package
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="version">Package version</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Returns .Net assembly references defined in a package</returns>
        [ExcelFunction(
            Name = "nugetGetReferences",
            Category = "Nuget (Info)",
            Description = "List Nuget package references",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] GetPackageReferences(string packageId, string version, string alias = "")
        {
            return FeedManager[alias][packageId, version].xlReferences();
        }

        /// <summary>
        /// Returns package authors
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="version">Package version</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Package authors</returns>
        [ExcelFunction(
            Name = "nugetGetAuthors",
            Category = "Nuget (Info)",
            Description = "List Nuget package authors",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object[,] GetPackageAuthors(string packageId, string version, string alias = "")
        {
            return FeedManager[alias][packageId, version].xlAuthors();
        }

        #endregion

        #region Package management

        /// <summary>
        /// Installs a package
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="version">(Optional) A package version. Omitting this parameter will used the latest version.</param>
        /// <param name="acceptLicense">(Optional) Set to true to accept license agreement. Default is false.</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Success/Failure</returns>
        [ExcelFunction(
            Name = "nugetInstall",
            Category = "Nuget (Management)",
            Description = "Install a Nuget package",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object InstallPackage(string packageId, string version, bool acceptLicense, string alias = "")
        {
            return FeedManager[alias].InstallPackage(packageId, version, acceptLicense);
        }

        /// <summary>
        /// Uninstalls a package
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="version">A package version</param>
        /// <param name="removeDependencies">(Optional) Set to true to remove dependencies</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Success/Failure</returns>
        [ExcelFunction(
            Name = "nugetUninstall",
            Category = "Nuget (Management)",
            Description = "Uninstall a Nuget package",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object UninstallPackage(
            string packageId, 
            string version, 
            bool removeDependencies, 
            string alias = "")
        {
            return FeedManager[alias].UninstallPackage(packageId, version, removeDependencies);
        }

        /// <summary>
        /// Update a package
        /// </summary>
        /// <param name="packageId">A package name</param>
        /// <param name="alias">(Optional) A feed alias. Default is used when omitted.</param>
        /// <returns>Success/Failure</returns>
        [ExcelFunction(
            Name = "nugetUpdate",
            Category = "Nuget (Management)",
            Description = "Update a Nuget package",
            IsExceptionSafe = false,
            IsHidden = false,
            IsMacroType = false,
            IsThreadSafe = false,
            IsVolatile = false)]
        public static object UpdatePackage(string packageId, string alias = "")
        {
            return FeedManager[alias].UpdatePackage(packageId);
        }


        #endregion

    }
}